﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Text.RegularExpressions;

using Roslyn.Compilers.CSharp;

using King.Declarative;
using King.Text;
using King.Text.Xml;
using King.Extensions;
using King.CSharp.Reflection;
using King.CSharp.Decompiler;

namespace King.CSharp.Html {
#if flase
    public sealed class SyntaxUri {

        private Uri m_root;
        private Uri m_relativeTo;
        private string m_language;

        public SyntaxUri(Uri root, Uri relativeTo = null, string language = "csharp") {
            m_root = root;
            m_relativeTo = relativeTo;
            m_language = language;
        }

        public Uri Root {
            get { return m_root; }
        }
        public string Language {
            get { return m_language; }
        }
        public Uri LanguageRoot {
            get { return new Uri(m_root, m_language + "/"); }
        }
        public Uri RelativeTo {
            get { return m_relativeTo; }
        }

        public Uri GetUri(CSharpAssemblySymbol assembly) {
            var fullName = Regex.Replace(assembly.AssemblyName.FullName, @"\s", string.Empty);
            return new Uri(LanguageRoot, fullName + "/");
        }
        public Uri GetUri(CSharpTypeSymbol type) {
            var result = new Uri(GetUri(type.Assembly), type.MetadataFullName + "/default.html");
            if (m_relativeTo != null)
                result = m_relativeTo.MakeRelativeUri(result);
            return result;
        }

        public SyntaxUri MakeRelativeTo(CSharpTypeSymbol type) {
            return new SyntaxUri(m_root, GetUri(type), m_language);
        }
    }

    public static class Extensions {

        private class SturctOrObjectEnumerator<T> : SturctEnumerator<T>, IEnumerable<object> where T : struct {

            internal SturctOrObjectEnumerator(IEnumerable<T> enumerable)
                : base(enumerable) {
            }

            public new IEnumerator<object> GetEnumerator() {
                return ((IEnumerable<T>)this).Select(o => (object)o).GetEnumerator();
            }
        }

        private class SturctEnumerator<T> : IEnumerable<T> where T : struct {
            private IEnumerable<T> m_enumerable;

            internal SturctEnumerator(IEnumerable<T> enumerable) {
                m_enumerable = enumerable;
            }

            public IEnumerator<T> GetEnumerator() {
                return m_enumerable.GetEnumerator();
            }
            IEnumerator IEnumerable.GetEnumerator() {
                return m_enumerable.GetEnumerator();
            }
        }

        public static IEnumerable Children(this SyntaxNode syntaxNode) {
            var children = syntaxNode.ChildNodesAndTokens();

            IEnumerable<object> result = null;

            if (children.All(o => o.IsNode))
                result = children.Select(o => o.AsNode());
            else if (children.All(o => o.IsToken))
                result = new SturctOrObjectEnumerator<SyntaxToken>(children.Select(o => o.AsToken()));
            else
                result = children.Select(o => (object)o.AsNode() ?? o.AsToken());

            return result;
        }
    }

    public class SyntaxHtml : TextDeclaration {

        private IEnumerable<CSharpTypeSymbol> m_types;
        private SyntaxUri m_resolver;
        private CSharpCompilation m_compilation;

        public SyntaxHtml(IEnumerable<CSharpTypeSymbol> types, SyntaxUri resolver, CSharpCompilation compilation) {
            m_types = types;
            m_compilation = compilation;
            m_resolver = resolver;
        }

        public SyntaxUri Resolver {
            get { return m_resolver; }
        }

        protected override IEnumerable Substance() {
            return base.Children();
        }
        protected override IEnumerable Children(DeclarationContext context) {
            var referencedTypes = new HashSet<CSharpTypeSymbol>();

            var code = new TextDeclaration() { 
                new TypeObserverDecompiler(referencedTypes) { Children() }
            }.ReadToEnd();

            var tree = SyntaxTree.ParseCompilationUnit(code);
            var compilationUnit = (CompilationUnitSyntax)tree.Root;

            var usings = new TextSingleSpaced(suffix: Environment.NewLine) {
                from o in referencedTypes.ToHashSet(o => o.Namespace)
                where o != null
                select new TextDeclaration { "using ", o, ";" }
            }.ReadToEnd();

            tree = SyntaxTree.Create("myFileName",
                compilationUnit.ReplaceNode(compilationUnit,
                    Syntax.CompilationUnit(
                        usings: ((CompilationUnitSyntax)SyntaxTree.ParseCompilationUnit(usings).Root).Usings,
                        attributes: compilationUnit.Attributes,
                        members: compilationUnit.Members
                    )
                )
            );

            var compilation = m_compilation; //.Add(tree);

            return new HtmlHtml() { 

                new HtmlHead() { 
                    new HtmlLink("stylesheet", "text/css", "../../style.css") 
                },

                new HtmlBody() {

                    new HtmlDiv(new HtmlCommonAttributes() { Id = "Nav" }) { 
                        
                        new HtmlUnorderedList { 
                                
                            from o in m_types
                            orderby o.MetadataFullName
                            select new HtmlListItem { 
                                new HtmlAnchor(m_resolver.GetUri(o)) {
                                    o.MetadataFullName 
                                }
                            }
                        }
                    },
                    new HtmlDiv(new HtmlCommonAttributes() { Id = "Main" }) {
                        new HtmlPreformatted() {

                            // Substitute SyntaxTokens and SyntaxTrivia with text and SyntaxNodes with it's children
                            new SyntaxGetText() {

                                // Add hyperlinks
                                new SyntaxLink(compilation, m_resolver) {

                                    // Substitute SyntaxTokens and SyntaxTrivia in order to color them
                                    new SyntaxColorer() { 

                                        // Substitute SyntaxNodes, SyntaxTokens, and SyntaxTrees with their children
                                        new SyntaxFormat(tree)
                                    }
                                }
                            }
                        }
                    }
                }
            };
        }
    }

    [Declaration(SubstituteTypes = new[] { 
        typeof(SyntaxNode),
        typeof(SyntaxToken),
        typeof(SyntaxTrivia)
    })]
    public class SyntaxGetText : TextDeclaration {
        protected override IEnumerable Substitute(DeclarationContext context, object value) {

            if (value is SyntaxToken) {
                var token = (SyntaxToken)value;
                var text = token.GetText();

                if (token.Parent is IdentifierNameSyntax && token.Parent.Parent is AttributeSyntax)
                    text = Regex.Replace(text, "Attribute$", string.Empty);

                yield return text;

            } else if (value is SyntaxNode) {
                var node = (SyntaxNode)value;
                yield return node.Children();

            } else {
                var triva = (SyntaxTrivia)value;
                yield return triva.GetText();
            }
        }
    }

    [Declaration(SubstituteTypes = typeof(SyntaxToken))]
    public class SyntaxLink : TextDeclaration {

        private CSharpCompilation m_compilation;
        private SyntaxUri m_resolver;

        public SyntaxLink(CSharpCompilation compilation, SyntaxUri resolver) {
            m_compilation = compilation;
            m_resolver = resolver;
        }

        protected override IEnumerable Substitute(DeclarationContext context, object value) {
            //var result = context.Substitute(value);
            
            //var token = (SyntaxToken)value;
            //if (token.Kind != SyntaxKind.IdentifierToken)
            //    return new[] { result };

            //var type = m_compilation.TryGetType(token.Parent);
            //if (type == null)
            //    return new[] { result };

            //type = type.GenericTypeDefinition;

            //return new HtmlAnchor(m_resolver.GetUri(type)) {
            //    result
            //};
            throw new NotImplementedException();
        }
    }

    [Declaration(SubstituteTypes = new[] { 
        typeof(SyntaxToken),
        typeof(SyntaxTrivia)
    })]
    public class SyntaxColorer : TextDeclaration {

        protected override IEnumerable Substitute(DeclarationContext context, object value) {

            var child = context.Substitute(value) ?? value;

            // tokens
            if (value is SyntaxToken) {
                var token = (SyntaxToken)value;

                if (Enum.GetName(typeof(SyntaxKind), token.Kind).Contains("Keyword"))
                    return new HtmlSpan(cls: "keyword") { child };

                if (token.Kind == SyntaxKind.StringLiteralExpression || token.Kind == SyntaxKind.StringLiteralToken)
                    return new HtmlSpan(cls: "string") { child };

                if (token.Kind == SyntaxKind.IdentifierToken) {
                    var node = token.Parent;
                    var nodeTypesToColor = new[] { 
                        typeof(GenericNameSyntax),
                        typeof(TypeDeclarationSyntax),
                    };

                    if (node is IdentifierNameSyntax) {
                        node = node.Parent;
                        nodeTypesToColor = new[] { 
                            typeof(AttributeSyntax),
                            typeof(ArgumentSyntax),
                            typeof(BaseListSyntax),
                        };
                    }

                    if (nodeTypesToColor.Any(o => o.IsAssignableFrom(node.GetType())))
                        return new HtmlSpan(cls: "identifier") { child };
                }

            // trivia
            } else {
                var trivia = (SyntaxTrivia)value;

                if (trivia.Kind == SyntaxKind.SingleLineCommentTrivia ||
                    trivia.Kind == SyntaxKind.MultiLineCommentTrivia)
                    return new HtmlSpan(cls: "comment") { child };
            }

            return null;
        }
    }

    [Declaration(SubstituteTypes = new[] { 
        typeof(SyntaxNode), 
        typeof(SyntaxToken), 
        typeof(SyntaxTree)
    })]
    public class SyntaxFormat : LeafTextDeclaration {

        public object m_syntax;

        public SyntaxFormat(object syntax) {
            m_syntax = syntax;
        }

        protected override IEnumerable Substitute(DeclarationContext context, object value) {

            if (value is SyntaxTree)
                value = ((SyntaxTree)value).Root;

            if (value is SyntaxToken) {
                var token = (SyntaxToken)value;
                var leadingTrivia = token.LeadingTrivia;
                var trailingTrivia = token.TrailingTrivia;

                yield return leadingTrivia;
                yield return context.Substitute(token);
                yield return trailingTrivia;

            } else {
                var node = (SyntaxNode)value;
                yield return context.Substitute(node);
            }
        }

        protected override IEnumerable Substance() {
            yield return m_syntax == null ? null : Substantial;
        }
        protected override IEnumerable Children(DeclarationContext context) {
            yield return m_syntax;
        }
    }
#endif
}
